package club.qweqwe.servlet;

import club.qweqwe.dao.FileLogDaoImpl;
import club.qweqwe.exception.FileException;
import club.qweqwe.exception.RequestException;
import club.qweqwe.listener.DatabaseConfiglistener;
import club.qweqwe.model.dbobj.FileInfo;
import club.qweqwe.model.dbobj.User;
import club.qweqwe.utils.ReflectUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import java.io.*;
import java.util.*;

@WebServlet(name = "FileServlet")
public class FileServlet extends HttpServlet implements ServletHelper {

    private String dirPath;
    private String tmpDirPath;
    private int fileThresholdMemeryMaxSize = 1024;
    private int fileMaxSize = 1024;
    private int requestMaxSize = 1024 * 5;
    private int maxFileNameLength = 48;
    private int maxUploadFileNameLength = 32;

    @Override
    public void init() throws ServletException {
        tmpDirPath = getServletContext().getInitParameter("UploadFileTmpDir");
        if (tmpDirPath == null)
            tmpDirPath = "./tmp/";

        dirPath = getServletContext().getInitParameter("UploadFileDir");
        if (dirPath == null)
            dirPath = "./upload/";


        String tmp = getServletContext().getInitParameter("FileThresholdMemeryMaxSize");
        try {
            if (tmp != null)
                fileThresholdMemeryMaxSize = Integer.parseInt(tmp);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        tmp = getServletContext().getInitParameter("FileMaxSize");
        try {
            if (tmp != null)
                fileMaxSize = Integer.parseInt(tmp);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        tmp = getServletContext().getInitParameter("RequestMaxSize");
        try {
            if (tmp != null)
                requestMaxSize = Integer.parseInt(tmp);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        dispatcher(request, response, "Post");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        dispatcher(request, response, "Get");
    }

    public void uploadPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, CloneNotSupportedException {
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart)
            throw RequestException.REQUEST_ERROR_CODE_PARAMER;

        HttpSession session = request.getSession(false);

        User user = (User) session.getAttribute(ReflectUtils.getClassName(User.class));

        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(fileThresholdMemeryMaxSize);

        File repository = new File(tmpDirPath);
        factory.setRepository(repository);

        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setFileSizeMax(fileMaxSize);
        upload.setSizeMax(requestMaxSize);
        List<FileItem> items;
        try {
            items = upload.parseRequest(request);
        } catch (FileUploadException e) {
            e.printStackTrace();
            throw FileException.FILE_ERROR_CODE_UPLOAD_FILE;
        }
        String uuid = user.getId();
        uuid = uuid.replace("-", "");
        String path = String.format("%s%s/", dirPath, uuid);

        File fileDir = new File(path);
        if (!fileDir.exists())
            fileDir.mkdirs();

        List<String> errorFileName = new LinkedList<>();
        Iterator<FileItem> iter = items.iterator();
        while (iter.hasNext()) {
            FileItem item = iter.next();

            if (!item.isFormField()) {
                String fileName = item.getName();
                if (maxUploadFileNameLength > fileName.length()) {
                    int nPreIndex = fileName.lastIndexOf('.');
                    String postfix = fileName.substring(nPreIndex);
                    String realFileName = processUploadedFile(postfix, item, path);
                    if (realFileName != null) {
                        //写入成功
                        DataSource dataSource = (DataSource) request.getServletContext().getAttribute(DatabaseConfiglistener.DATABASE_KEY);
                        FileLogDaoImpl fileLogDao = new FileLogDaoImpl(dataSource);
                        fileLogDao.addFile(user.getId(), item.getName(), realFileName);
                        continue;
                    }
                }

                errorFileName.add(item.getName());
            }
        }
        if (errorFileName.size() > 0) {
            FileException e = new FileException(FileException.FILE_ERROR_CODE_UPLOAD_FILE);
            e.setFileNames(errorFileName);
            throw e;
        }
    }

    private String processUploadedFile(String postfix, FileItem fileItem, String dirPath) {
        File file = null;
        try (InputStream inputStream = fileItem.getInputStream()) {
            String uuid = UUID.randomUUID().toString();
            uuid = uuid.replace("-", "");

            String realFileName = uuid + postfix;
            if (realFileName.length() > maxFileNameLength)
                realFileName = realFileName.substring(realFileName.length() - maxFileNameLength);
            String pathName = dirPath + realFileName;

            file = new File(pathName);
            if (file.createNewFile()) {
                try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
                    byte[] bytes = new byte[1024];
                    int len = 0;
                    while (0 < (len = inputStream.read(bytes))) {
                        fileOutputStream.write(bytes, 0, len);
                    }
                }
                return file.getName();
            }
        } catch (IOException e) {
            if (file != null) {
                file.delete();
            }
            e.printStackTrace();
        }

        return null;
    }
}
